﻿using Chinese;
using DependencyInjection.StaticAccessor;
using FreeSql.Aop;
using FreeSql.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace FreeSql
{

    /// <summary>
    /// 
    /// </summary>
    public static class DbHelper
    {


        /// <summary>
        /// 偏移时间
        /// </summary>
        public static TimeSpan TimeOffset;



        /// <summary>
        /// 获得指定程序集表实体获取enties 
        /// </summary>
        /// <returns></returns>
        public static Type[] GetTypesByTableAttribute()
        {
            List<Type> tableAssembies = new List<Type>();
            var ass = Assembly.GetAssembly(typeof(BaseEntity));

            foreach (Type type in ass.GetExportedTypes())
            {
                foreach (Attribute attribute in type.GetCustomAttributes(false))
                {
                    if (attribute is TableAttribute tableAttribute)
                    {
                        if (tableAttribute.DisableSyncStructure == false)
                        {
                            tableAssembies.Add(type);
                        }
                    }
                    //else if (attribute is System.ComponentModel.DataAnnotations.Schema.TableAttribute)
                    //{
                    //    tableAssembies.Add(type);
                    //}
                }
            }
            ;
            return tableAssembies.ToArray();
        }


        public static Type[] GetTypesByTableAttribute(Assembly ass)
        {
            List<Type> tableAssembies = new List<Type>();
            foreach (Type type in ass.GetExportedTypes())
            {
                foreach (Attribute attribute in type.GetCustomAttributes(false))
                {
                    if (attribute is TableAttribute tableAttribute)
                    {
                        if (tableAttribute.DisableSyncStructure == false)
                        {
                            tableAssembies.Add(type);
                        }
                    }
                    //else if (attribute is System.ComponentModel.DataAnnotations.Schema.TableAttribute)
                    //{
                    //    tableAssembies.Add(type);
                    //}
                }
            }
            ;
            return tableAssembies.ToArray();
        }
        /// <summary>
        /// 配置实体
        /// </summary>
        public static void SyncStructure(IFreeSql db, Assembly ass)
        {
            //获得指定程序集表实体
            var entityTypes = GetTypesByTableAttribute(ass);
            foreach (var entityType in entityTypes)
            {

                db.CodeFirst.Entity(entityType, a =>
                {

                });
            }

            db.CodeFirst.SyncStructure(entityTypes);

        }
        /// <summary>
        /// 配置实体
        /// </summary>
        public static void ConfigEntity(IFreeSql db)
        {
            //获得指定程序集表实体
            var entityTypes = GetTypesByTableAttribute();
            foreach (var entityType in entityTypes)
            {

                db.CodeFirst.Entity(entityType, a =>
                {

                });
            }

            db.CodeFirst.SyncStructure(entityTypes);

        }



        /// <summary>
        /// 审计数据
        /// </summary>
        /// <param name="e"></param>
        public static void AuditValue(AuditValueEventArgs e)
        {
            IAuthUser user = PinnedScope.ScopedServices?.GetRequiredService<IAuthUser>() ?? throw new Exception("IAuthUser未注入");

            if (e.Property.Name == "Chinese" && (e.AuditValueType == AuditValueType.Insert
                    || e.AuditValueType == AuditValueType.InsertOrUpdate
                    || e.AuditValueType == AuditValueType.Update))//chinese
            {
                var other = e.Property.GetCustomAttribute<OtherAttribute>();
                if (other != null)
                {
                    var name = other.OtherFieldName;
                    var nvalue = e?.Object?.GetType()?.GetProperty(name)?.GetValue(e.Object, null);

                    if (nvalue != null)
                    {
                        e.Value = Pinyin.GetString(nvalue?.ToString() ?? "", PinyinFormat.InitialConsonant);
                    }
                }
            }
            if (e.AuditValueType == AuditValueType.Insert
                && e.Property.Name == "Id"
                && e.Property.GetCustomAttribute<SnowflakeAttribute>(false) != null
                && (e.Value == null || e.Value?.ToString() == "0"
                || e.Value?.ToString() == "" || e.Value?.ToString() == Guid.Empty.ToString())

                )
            {

                if (e.Property.PropertyType == typeof(string))
                {
                    e.Value = IdHelper.NextId().ToString();
                }
                else if (e.Property.PropertyType == typeof(long))
                {
                    e.Value = IdHelper.NextId();
                }
                else if (e.Property.PropertyType == typeof(Guid))
                {
                    e.Value = Guid.NewGuid();
                }
                else throw new Exception("不支持的主键类型");
            }

            //if (user == null || user.Id == 0)
            //{
            //    return;
            //}

            if (e.AuditValueType == AuditValueType.Insert)
            {

                switch (e.Property.Name)
                {
                    //case "No":
                    //    e.sql
                    //    break;
                    case "CreatedBy":
                        if (e.Value == null || (long)e.Value <= 0) e.Value = user.Id;
                        break;
                    case "CreatedByName":
                        if (e.Value == null || e.Value.ToString() == "") e.Value = user.UserName;
                        break;
                    case "OrgId":
                        if (e.Value == null || (long)e.Value <= 0)
                        {
                            var orgId = user.OrgId;
                            if (orgId > 0)
                                e.Value = orgId;
                        }
                        break;
                    case "TenantId":
                        if (e.Value == null || (long)e.Value <= 0)
                        {
                            if (TenantContext.Tenant != null && TenantContext.Tenant.Id > 0)
                            {
                                e.Value = TenantContext.Tenant.Id;
                            }
                        }
                        break;
                    case "CreatedTime":
                        e.Value = DateTime.Now;
                        break;
                    case "UpdatedBy":
                        e.Value = user.Id;
                        break;
                    case "UpdatedByName":
                        e.Value = user.UserName;
                        break;
                    case "UpdatedTime":
                        e.Value = DateTime.Now;
                        break;
                }
            }
            else if (e.AuditValueType == FreeSql.Aop.AuditValueType.Update)
            {
                switch (e.Property.Name)
                {
                    case "UpdatedBy":
                        e.Value = user.Id;
                        break;
                    case "UpdatedByName":
                        e.Value = user.UserName;
                        break;
              
                    case "UpdatedTime":
                        e.Value = DateTime.Now;
                        break;
                }
            }
            else if (e.AuditValueType == FreeSql.Aop.AuditValueType.InsertOrUpdate)
            {
                switch (e.Property.Name)
                {
                    case "CreatedBy":
                        if (e.Value == null || (long)e.Value <= 0) e.Value = user.Id;
                        break;
                    case "CreatedByName":
                        if (e.Value == null || e.Value.ToString() == "") e.Value = user.UserName;
                        break;
                    case "UpdatedBy":
                        e.Value = user.Id;
                        break;
                    case "UpdatedByName":
                        e.Value = user.UserName;
                        break;
                    case "TenantId":
                        if (e.Value == null || (long)e.Value <= 0)
                        {
                            if (TenantContext.Tenant != null && TenantContext.Tenant.Id > 0)
                            {
                                e.Value = TenantContext.Tenant.Id;
                            }
                        }
                        break;
                    case "OrgId":
                        if (e.Value == null || (long)e.Value <= 0)
                        {
                            var orgId = user.OrgId;
                            if (orgId > 0)
                                e.Value = orgId;
                        }
                        break;
                    case "CreatedTime":
                        e.Value = DateTime.Now;
                        break;
                    case "UpdatedTime":
                        e.Value = DateTime.Now;
                        break;
                }
            }
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="s"></param>
        /// <param name="e"></param>
        public static void ConfigEntityProperty(object? s, ConfigEntityPropertyEventArgs e)
        {
            // 处理排序字段自动取最大值插入
            if (s is IFreeSql fsql)
            {
                if (e.Property.GetCustomAttributes(typeof(MaxValueAttribute)).Any())
                {
                    var max = e.Property.GetCustomAttribute(typeof(MaxValueAttribute)) as MaxValueAttribute;

                    var entityTypeAttr = (e.EntityType.GetCustomAttribute(typeof(TableAttribute)) as FreeSql.DataAnnotations.TableAttribute);
                    string tableName = string.IsNullOrWhiteSpace(entityTypeAttr?.Name) ? e.EntityType.Name : entityTypeAttr.Name; // 取得表名

                    var PropertyAttr = (e.Property.GetCustomAttribute(typeof(ColumnAttribute)) as FreeSql.DataAnnotations.ColumnAttribute);


                    string fieldName = string.IsNullOrWhiteSpace(PropertyAttr?.Name) ? e.ModifyResult.Name : PropertyAttr.Name; // 取得字段名
                    var hasPre = !string.IsNullOrWhiteSpace(max?.Prefix) || !string.IsNullOrWhiteSpace(max?.DateFormat);


                    /*var tableInfo = fsql.CodeFirst.GetTableByEntity(e.EntityType);


                    string whereSql = "";
                    if (tableInfo.ColumnsByPosition.Any(x => x.CsName == nameof(ITenant.TenantId)))
                    {

                      // tableInfo.Columns.FirstOrDefault(x => x.Key == "").Value.DbSize;
                        whereSql += "";
                    }; */

                    // fsql.Select<object>().AsType(e.EntityType).Max("")




                    if (!string.IsNullOrWhiteSpace(fieldName))
                    {

                        string insertValueSql = "";
                        switch (fsql.Ado.DataType)
                        {
                            case DataType.MySql:
                            case DataType.OdbcMySql:
                                if (max?.Sql != null && max.Sql.Count > 0)
                                    insertValueSql = GetSql(0, max?.Sql);
                                else if (max?.Len != 0 && hasPre)
                                    insertValueSql = $"(SELECT CONCAT('{max.Prefix}',LPAD(a.max_v, {max.Len} , \"0\")) FROM (SELECT (IFNULL(max({fieldName}),0) + 1) max_v from {tableName}) a)";
                                else insertValueSql = $"(SELECT a.max_v FROM (SELECT (IFNULL(max({fieldName}),0) + 1) max_v from {tableName}) a)";
                                break;
                            case DataType.SqlServer:
                            case DataType.OdbcSqlServer:
                                if (max?.Sql != null && max.Sql.Count > 0)
                                    insertValueSql = GetSql(1, max?.Sql);
                                else if (max?.Len != 0 && hasPre)
                                    insertValueSql = $"(SELECT '{max.Prefix}'+RIGHT('{"0".PadLeft(max.Len, '0')}'+CAST(a.max_v AS nvarchar(50)),{max.Len}) FROM (SELECT (isnull(max({fieldName}),0) + 1) max_v from {tableName}) a)";
                                else insertValueSql = $"(SELECT a.max_v FROM (SELECT (isnull(max({fieldName}),0) + 1) max_v from {tableName}) a)";
                                break;
                            case DataType.PostgreSQL:
                            case DataType.OdbcPostgreSQL:
                                if (max?.Sql != null && max.Sql.Count > 0)
                                    insertValueSql = GetSql(2, max?.Sql);
                                else if (max?.Len != 0 && hasPre)
                                {
                                    insertValueSql = $"(SELECT CONCAT('{max.Prefix}',lpad( cast(a.max_v as VARCHAR),{max.Len},'0')) FROM (SELECT (COALESCE(max(cast(right(\"{fieldName}\",{max.Len}) as numeric)),0) + 1) max_v from \"{tableName}\") a)";
                                }
                                else  //lpad(a.max_v, 6, '0')
                                    insertValueSql = $"(SELECT a.max_v FROM (SELECT (COALESCE(max(\"{fieldName}\"),0) + 1) max_v from \"{tableName}\") a)";
                                break;
                            case DataType.Oracle:
                            case DataType.OdbcOracle:
                                if (max?.Sql != null && max.Sql.Count > 0)
                                    insertValueSql = GetSql(3, max?.Sql);
                                else if (max?.Len != 0 && hasPre)
                                    insertValueSql = $"(SELECT  concat('{max.Prefix}',lpad(a.max_v,{max.Len},0)) FROM (SELECT (nvl(max({fieldName}),0) + 1) max_v from {tableName}) a)";
                                else
                                    insertValueSql = $"(SELECT a.max_v FROM (SELECT (nvl(max({fieldName}),0) + 1) max_v from {tableName}) a)";
                                break;
                            default:
                                insertValueSql = $"(SELECT a.max_v FROM (SELECT (max({fieldName}) + 1) max_v from {tableName}) a)";
                                break;
                        }
                        e.ModifyResult.InsertValueSql = insertValueSql;
                    }
                    else
                    {
                        Console.WriteLine($"max fieldName不能为空");
                    }
                }

            }
        }
        static string GetSql(int key, Dictionary<int, string>? sql)
        {
            var s = sql?.FirstOrDefault(x => x.Key == key).Value ?? "";
            if (string.IsNullOrWhiteSpace(s))
            {
                throw new Exception("插入语句为空");
            }
            return s;
        }
    }
}
